﻿using System;
using System.Linq;
using Remotion.Linq;
using Remotion.Linq.Clauses;
using Remotion.Linq.Clauses.ResultOperators;
using System.Diagnostics;

namespace Gumini.JulaDB.Linq
{
    public class JulaQueryModelVisitor : QueryModelVisitorBase
    {
        private readonly QueryPartsAggregator _queryParts = new QueryPartsAggregator();

        public JulaQueryModelVisitor()
        {
            Debug.WriteLine("JulaQueryModelVisitor::ctor");
        }

        public static string GenerateQuery(QueryModel queryModel)
        {
            var visitor = new JulaQueryModelVisitor();
            visitor.VisitQueryModel(queryModel);
            return visitor.GetCommand();
        }

        public string GetCommand()
        {
            return _queryParts.BuildString();
        }

        public override void VisitQueryModel(QueryModel queryModel)
        {
            Debug.WriteLine("JulaQueryModelVisitor::VisitQueryModel");
            queryModel.SelectClause.Accept(this, queryModel);
            queryModel.MainFromClause.Accept(this, queryModel);
            VisitBodyClauses(queryModel.BodyClauses, queryModel);
            VisitResultOperators(queryModel.ResultOperators, queryModel);
        }

        public override void VisitResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, int index)
        {
            Debug.WriteLine("JulaQueryModelVisitor::VisitResultOperator");
            if (resultOperator is CountResultOperator)
                _queryParts.SelectPart = string.Format("count({0})", _queryParts.SelectPart);
            else if (resultOperator is MinResultOperator)
                _queryParts.SelectPart = string.Format("min({0})", _queryParts.SelectPart);
            else if (resultOperator is MaxResultOperator)
                _queryParts.SelectPart = string.Format("max({0})", _queryParts.SelectPart);
            else if (resultOperator is SumResultOperator)
                _queryParts.SelectPart = string.Format("sum({0})", _queryParts.SelectPart);
            else if (resultOperator is AverageResultOperator)
                _queryParts.SelectPart = string.Format("avg({0})", _queryParts.SelectPart);
            else if (resultOperator is DistinctResultOperator)
                _queryParts.IsDistinct = true;
            else
                throw new NotSupportedException(String.Format("Result operator {0} is not supported.", resultOperator.ToString()));

            base.VisitResultOperator(resultOperator, queryModel, index);
        }

        public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel)
        {
            Debug.WriteLine("JulaQueryModelVisitor::VisitMainFromClause");
            _queryParts.AddFromPart(fromClause);

            base.VisitMainFromClause(fromClause, queryModel);
        }

        public override void VisitAdditionalFromClause(AdditionalFromClause fromClause, QueryModel queryModel, int index)
        {
            Debug.WriteLine("JulaQueryModelVisitor::VisitAdditionalFromClause");
            _queryParts.AddFromPart(fromClause);

            base.VisitAdditionalFromClause(fromClause, queryModel, index);
        }

        public override void VisitSelectClause(SelectClause selectClause, QueryModel queryModel)
        {
            Debug.WriteLine("JulaQueryModelVisitor::VisitSelectClause");
            _queryParts.SelectPart = JulaExpressionTreeVisitor.GetExpressionString(selectClause.Selector);

            base.VisitSelectClause(selectClause, queryModel);
        }

        public override void VisitWhereClause(WhereClause whereClause, QueryModel queryModel, int index)
        {
            Debug.WriteLine("JulaQueryModelVisitor::VisitWhereClause");
            _queryParts.AddWherePart(JulaExpressionTreeVisitor.GetExpressionString(whereClause.Predicate));

            base.VisitWhereClause(whereClause, queryModel, index);
        }

        public override void VisitOrderByClause(OrderByClause orderByClause, QueryModel queryModel, int index)
        {
            Debug.WriteLine("JulaQueryModelVisitor::VisitOrderByClause");
            foreach(var o in orderByClause.Orderings)
                _queryParts.AddOrderByPart(JulaExpressionTreeVisitor.GetExpressionString(o.Expression), o.OrderingDirection);

            base.VisitOrderByClause(orderByClause, queryModel, index);
        }

        public override void VisitJoinClause(JoinClause joinClause, QueryModel queryModel, int index)
        {
            Debug.WriteLine("JulaQueryModelVisitor::VisitJoinClause");
            _queryParts.AddFromPart(joinClause);
            _queryParts.AddWherePart(
                "({0} = {1})",
                JulaExpressionTreeVisitor.GetExpressionString(joinClause.OuterKeySelector),
                JulaExpressionTreeVisitor.GetExpressionString(joinClause.InnerKeySelector));

            base.VisitJoinClause(joinClause, queryModel, index);
        }
    }
}